﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Algobox.Feeds.Finance.MarketData;

namespace Algobox.Structure.Finance.Strategies.FractionPrototype
{
    public class FractionTrigger : INotifyPropertyChanged, IMarketUpdateLast
    {
        public FractionTrigger(IFractionComponent parent)
        {
            Father = parent;
            Value = parent.Value;
            Trigger = parent.Trigger;
            ExitTrigger = parent.Exit;

            FractionComponentDay day = parent as FractionComponentDay;
            if (day != null)
            {
                TriggerType = "Day";
                Entry = day.DayDifference;
                MidA = day.MidA;
                MidB = day.MidB;
                DayMoveA = day.DayMoveA;
                DayMoveB = day.DayMoveB;
                DayDifference = day.DayDifference;
            }
            else
            {
                FractionComponentOpen open = parent as FractionComponentOpen;
                TriggerType = "Open";
                Entry = open.OpenDifference;
                OpenA = open.OpenA;
                OpenB = open.OpenB;
                OpenMoveA = open.OpenMoveA;
                OpenMoveB = open.OpenMoveB;
                OpenDifference = open.OpenDifference;
            }

            // simulation for now
            // went long the spread?
            if (Entry >= Trigger)
            {
                _isLongSpread = true;

                PriceAIn = parent.Security.Prices.Bid.Price;
                QuantityAIn = (int)(-Value / (PriceAIn / 100));

                PriceBIn = parent.SecurityPair.Prices.Ask.Price;
                QuantityBIn = (int)(Value / (PriceBIn / 100));
            }
            // assume went short the spread
            else
            {
                _isLongSpread = false;

                PriceAIn = parent.Security.Prices.Ask.Price;
                QuantityAIn = (int)(Value / (PriceAIn / 100));

                PriceBIn = parent.SecurityPair.Prices.Bid.Price;
                QuantityBIn = (int)(-Value / (PriceBIn / 100));
            }

            PositionA = QuantityAIn;
            PositionB = QuantityBIn;

            TimeIn = DateTime.Now.TimeOfDay;

            // prime the mids
            TriggerMidA = (Father.Security.Prices.Ask.Price - Father.Security.Prices.Bid.Price) + Father.Security.Prices.Bid.Price;
            TriggerMoveA = (TriggerMidA - Father.Security.Prices.OHLC.Close) / Father.Security.Prices.OHLC.Close;
            TriggerMidB = (Father.SecurityPair.Prices.Ask.Price - Father.SecurityPair.Prices.Bid.Price) + Father.SecurityPair.Prices.Bid.Price;
            TriggerMoveB = (TriggerMidB - Father.SecurityPair.Prices.OHLC.Close) / Father.SecurityPair.Prices.OHLC.Close;
            TriggerDifference = (TriggerMoveA - TriggerMoveB) * 100;
            
            // potential threading issue
            // only subscribe once in a position
            parent.Manager.MarketFeed.SubscribeLast(parent.Security, this);
            parent.Manager.MarketFeed.SubscribeLast(parent.SecurityPair, this);
        }

        private static readonly NLog.Logger LOG = NLog.LogManager.GetCurrentClassLogger();

        private double _exitTrigger;
        private bool _isLongSpread;
        private bool _isComplete;

        public IFractionComponent Father { get; private set; }
        
        public string SymbolA { get { return Father.SymbolA; } }
        public string SymbolB { get { return Father.SymbolB; } }
        public double CloseA { get { return Father.CloseA; } }
        public double CloseB { get { return Father.CloseA; } }

        public double OpenA { get; private set; }
        public double OpenB { get; private set; }
        public double OpenMoveA { get; private set; }
        public double OpenMoveB { get; private set; }
        public double OpenDifference { get; private set; }

        public double MidA { get; private set; }
        public double MidB { get; private set; }
        public double DayMoveA { get; private set; }
        public double DayMoveB { get; private set; }
        public double DayDifference { get; private set; }

        public double TriggerMidA { get; private set; }
        public double TriggerMidB { get; private set; }
        public double TriggerMoveA { get; private set; }
        public double TriggerMoveB { get; private set; }
        public double TriggerDifference { get; private set; }

        public double Entry { get; private set; }
        public double Exit { get; private set; }

        public ushort Value { get; private set; }
        public string TriggerType { get; private set; }
        public double Trigger { get; private set; }

        public double ExitTrigger
        {
            get { return _exitTrigger; }
            set
            {
                if (value > 0 && value <= 100)
                {
                    _exitTrigger = value;
                    NotifyPropertyChanged("ExitTrigger");
                }
            }
        }

        public TimeSpan TimeIn { get; private set; }
        public TimeSpan TimeOut { get; private set; }
        public int PositionA { get; private set; }
        public int PositionB { get; private set; }
        public int QuantityAIn { get; internal set; }
        public int QuantityAOut { get; internal set; }
        public int QuantityBIn { get; internal set; }
        public int QuantityBOut { get; internal set; }
        public double PriceAIn { get; internal set; }
        public double PriceAOut { get; internal set; }
        public double PriceBIn { get; internal set; }
        public double PriceBOut { get; internal set; }
        public double PnL
        {
            get
            {
                return
                    ((QuantityAIn * (-PriceAIn / 100)) +
                    (QuantityBIn * (-PriceBIn / 100)) +
                    (QuantityAOut * (-PriceAOut / 100)) +
                    (QuantityBOut * (-PriceBOut / 100)) +
                    (-PositionA * (-Father.Security.Prices.Last.Price / 100)) +
                    (-PositionB * (-Father.SecurityPair.Prices.Last.Price / 100)))
                    ;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void ExitA()
        {
            if (PositionA == 0)
                return;

            TimeOut = DateTime.Now.TimeOfDay;

            if (PositionA > 0)            
                PriceAOut = Father.Security.Prices.Bid.Price;
            else
                PriceAOut = Father.Security.Prices.Ask.Price;

            QuantityAOut = -PositionA;
            PositionA = 0;

            NotifyPropertyChanged("TimeOut");
            NotifyPropertyChanged("PositionA");
            NotifyPropertyChanged("PriceAOut");
            NotifyPropertyChanged("QuantityAOut");
            NotifyPropertyChanged("PnL");

            if (PositionB == 0)
            {
                _isComplete = true;
                Father.OnFractionTriggerComplete(this);
            }
        }

        public void ExitB()
        {
            if (PositionB == 0)
                return;

            TimeOut = DateTime.Now.TimeOfDay;

            if (PositionB > 0)
                PriceBOut = Father.SecurityPair.Prices.Bid.Price;
            else
                PriceBOut = Father.SecurityPair.Prices.Ask.Price;

            QuantityBOut = -PositionB;
            PositionB = 0;            

            NotifyPropertyChanged("TimeOut");
            NotifyPropertyChanged("PositionB");
            NotifyPropertyChanged("PriceBOut");
            NotifyPropertyChanged("QuantityBOut");
            NotifyPropertyChanged("PnL");

            if (PositionA == 0)
            {
                _isComplete = true;
                Father.OnFractionTriggerComplete(this);
            }
        }

        public void OnPriceLast(Objects.Finance.Assets.Stock.IStock stock, Objects.Finance.Prices.PriceVolume last)
        {
            if (_isComplete)
            {
                Father.Manager.MarketFeed.UnsubscribeLast(Father.Security, this);
                Father.Manager.MarketFeed.UnsubscribeLast(Father.SecurityPair, this);
            }
            else
            {
                NotifyPropertyChanged("PnL");
                double mid;
                if (stock == Father.Security)
                {
                    mid = (Father.Security.Prices.Ask.Price - Father.Security.Prices.Bid.Price) + Father.Security.Prices.Bid.Price;
                    if (mid != TriggerMidA)
                    {
                        TriggerMidA = mid;

                        TriggerMoveA = (mid - Father.Security.Prices.OHLC.Close) / Father.Security.Prices.OHLC.Close;
                        TriggerDifference = (TriggerMoveA - TriggerMoveB) * 100;

                        CheckExit();
                        NotifyPropertyChanged("TriggerMidA");
                        NotifyPropertyChanged("TriggerMoveA");
                        NotifyPropertyChanged("TriggerDifference");
                    }
                }
                else if (stock == Father.SecurityPair)
                {
                    mid = (Father.SecurityPair.Prices.Ask.Price - Father.SecurityPair.Prices.Bid.Price) + Father.SecurityPair.Prices.Bid.Price;
                    if (mid != TriggerMidB)
                    {
                        TriggerMidB = mid;

                        TriggerMoveB = (mid - Father.SecurityPair.Prices.OHLC.Close) / Father.SecurityPair.Prices.OHLC.Close;
                        TriggerDifference = (TriggerMoveA - TriggerMoveB) * 100;

                        CheckExit();
                        NotifyPropertyChanged("TriggerMidB");
                        NotifyPropertyChanged("TriggerMoveB");
                        NotifyPropertyChanged("TriggerDifference");
                    }
                }
            }
        }
        
        private bool CheckExit()
        {
            if ((Entry > 0 && TriggerDifference <= _exitTrigger)
                || (Entry < 0 && TriggerDifference >= _exitTrigger))
            {                
                ExitA();
                ExitB();

                Exit = TriggerDifference;

                Father.Manager.MarketFeed.SubscribeLast(Father.Security, this);
                Father.Manager.MarketFeed.SubscribeLast(Father.SecurityPair, this);
                return true;
            }
            return false;
        }


    }
}
